package net.avcompris.binding.impl;

import static com.avcompris.util.ExceptionUtils.nonNullArgument;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.substringAfter;

import java.lang.reflect.Method;

import javax.xml.namespace.QName;

import com.avcompris.common.annotation.Nullable;

/**
 * default implementation of {@link BinderXPathVariableResolver}, used for
 * instance for bridges between DOM and avc-binding-common.
 * 
 * @author David Andrianavalontsalama
 */
final class DefaultBinderXPathVariableResolver implements
		BinderXPathVariableResolver {

	@Override
	public final void setThisNode(final Object rootNode) {

		this.rootNode = nonNullArgument(rootNode, "rootNode ");
	}

	public DefaultBinderXPathVariableResolver(final Object rootNode,
			final Method method, @Nullable final Object... params) {

		this.method = nonNullArgument(method, "method");
		this.params = params;

		setThisNode(rootNode);
	}

	private Object rootNode;
	private final Method method;
	@Nullable
	private final Object[] params;

	@Override
	public Object resolveVariable(final QName qName) {

		nonNullArgument(qName, "qName");

		final String name = qName.getLocalPart();

		if ("methodName".equals(name)) {

			return method.getName();

		} else if ("this".equals(name)) {

			return rootNode;
		}

		final int argIndex = extractArgIndexFromVariableName(qName);

		if (params == null || argIndex < 0 || argIndex >= params.length) {

			throw new IllegalArgumentException("Unknown XPath variable: "
					+ name + " (QName: " + qName + ", method: " + method + ")");
		}

		return params[argIndex];
	}

	/**
	 * extract the arg index held within a XPath variable name. Such recognized
	 * indexes are:
	 * <ul>
	 * <li>variable name = <tt>"arg0"</tt> =&gt; index = 0
	 * <li>variable name = <tt>"arg0-toto"</tt> =&gt; index = 0
	 * <li>variable name = <tt>"arg0_toto"</tt> =&gt; index = 0
	 * <li>variable name = <tt>"0"</tt> =&gt; index = 0
	 * <li>variable name = <tt>"0-toto"</tt> =&gt; index = 0
	 * <li>variable name = <tt>"0_toto"</tt> =&gt; index = 0
	 * <li>Otherwise: The method return an index of -1
	 * </ul>
	 */
	private static int extractArgIndexFromVariableName(final QName qName) {

		nonNullArgument(qName, "qName");

		final String name = qName.getLocalPart();

		if (isBlank(name)) {

			throw new IllegalArgumentException("XPath variable is empty: "
					+ name + " (QName: " + qName + ")");
		}

		String s = name;

		if (s.startsWith("arg")) {

			s = substringAfter(s, "arg");
		}

		if (!Character.isDigit(s.charAt(0))) {

			return -1;
		}

		final int length = s.length();

		for (int i = 1; i < length; ++i) {

			if (!Character.isDigit(s.charAt(i))) {

				s = s.substring(0, i);

				break;
			}
		}

		final int index;

		try {

			index = Integer.parseInt(s);

		} catch (final NumberFormatException e) {

			throw new IllegalArgumentException("Unknown XPath variable: "
					+ name + " (QName: " + qName + ")", e);
		}

		return index;
	}
}
